/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2004
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
*
* Author's email : insane@dees.ufmg.br
* Author's Website : http://www.dees.ufmg.br/insane
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package br.ufmg.dees.insane.model.xfemModel.geometryEntity;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import br.ufmg.dees.insane.model.femModel.elementNode.ElementNode;
import br.ufmg.dees.insane.model.xfemModel.xfemElement.XFEMElement;
import br.ufmg.dees.insane.model.xfemModel.xfemelementNode.XFEMElementNode;
import br.ufmg.dees.insane.util.IMatrix;
import br.ufmg.dees.insane.util.IPoint3d;
import br.ufmg.dees.insane.util.xfem.Segment;

/**
 * @author Kelson Wolff
 *
 */
public class Vertex extends GeometryEntity {
	
	/**
	 * Comment for <code>serialVersionUID</code>
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * The coordinates of this Vertex.
	 */
	private double[] coordinates;
	
	/**
	 * The associated PiecewiseLinear Geometry Entity that contain this Vertex.
	 */
	private PiecewiseLinear myParent;
	
	/**
	 * 
	 */
	public Vertex() {
		coordinates = new double[3];
		coordinates[0] = 0.0;
		coordinates[1] = 0.0;
		coordinates[2] = 0.0;
		
		myParent = new PiecewiseLinear();
	}

	/**
	 * @return Returns the coordinates.
	 */
	public double[] getCoordinates() {
		return coordinates;
	}
	
	/**
	 * @param i The position of the coordinate.
	 * @return Returns the coordinates at position i.
	 */
	public double getCoordinates(int i) {
		return coordinates[i];
	}

	/**
	 * @param coordinates The coordinates to set.
	 */
	public void setCoordinates(double[] coordinates) {
		this.coordinates = coordinates;
	}
	
	/**
	 * @param x The coordinate x to set.
	 * @param y The coordinate y to set.
	 */
	public void setCoordinates(double x, double y) {
		coordinates[0] = x;
		coordinates[1] = y;
	}

	/**
	 * @return Returns the myParent.
	 */
	public PiecewiseLinear getMyParent() {
		return myParent;
	}

	/**
	 * @param myParent The myParent to set.
	 */
	public void setMyParent(PiecewiseLinear myParent) {
		this.myParent = myParent;
	}

	/* (non-Javadoc)
	 * @see br.ufmg.dees.insane.model.xfemModel.geometryEntity.GeometryEntity#intersects(br.ufmg.dees.insane.util.xfem.Segment)
	 */
	@Override
	public boolean intersects(Segment s) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see br.ufmg.dees.insane.model.xfemModel.geometryEntity.GeometryEntity#intersection(br.ufmg.dees.insane.util.xfem.Segment)
	 */
	@Override
	public List<IPoint3d> intersection(Segment s) {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see br.ufmg.dees.insane.model.xfemModel.geometryEntity.GeometryEntity#interactsWith(br.ufmg.dees.insane.model.xfemModel.xfemElement.XFEMElement)
	 * Returns true if this vertex is inside the provided XFEMElement e and false if it isn't.
	 */
	@Override
	public boolean interactsWith(XFEMElement e) {
		int count = 0;
		double EPSILON = 1e-8;
		
		double x0 = this.getCoordinates(0);
		double y0 = this.getCoordinates(1);
		
		double delta,x1,y1,x2,y2;
		
		e.setCCWIncidence();
		
		int numNodes = e.getNbNodes();
		
		for(int i=0; i<numNodes; i++){
			x1 = e.getIncidence().get(i).getX();
			y1 = e.getIncidence().get(i).getY();
			
			if(i != (numNodes - 1)){
				x2 = e.getIncidence().get(i+1).getX();
				y2 = e.getIncidence().get(i+1).getY();
			}
			else{
				x2 = e.getIncidence().get(0).getX();
				y2 = e.getIncidence().get(0).getY();
			}
			
			delta = (x1-x0) * (y2-y0) - (x2-x0) * (y1-y0);
			
			if(delta > EPSILON)
				count += 1;
		}
		return (count == numNodes);
	}
	
	/**
	 * Returns true if this Vertex is inside provided 
	 * XFEMElement e or false if it is outside e.
	 * @param e The provided XFEMElement e.
	 * @return True if this Vertex is inside provided 
	 * XFEMElement e or false if it isn't.
	 */
	public boolean isInside(XFEMElement e){
		double x = this.getCoordinates(0);
		double y = this.getCoordinates(1);
		
		double tolerance = 1e-6;
		int count = 0;
		
		double delta,x0,y0,x1,y1;
		
		e.setCCWIncidence();
		
		int numNodes = e.getNbNodes();
		
		for(int i=0; i<numNodes; i++){
			x0 = e.getIncidence().get(i).getX();
			y0 = e.getIncidence().get(i).getY();
			
			if(i != (numNodes - 1)){
				x1 = e.getIncidence().get(i+1).getX();
				y1 = e.getIncidence().get(i+1).getY();
			}
			else{
				x1 = e.getIncidence().get(0).getX();
				y1 = e.getIncidence().get(0).getY();
			}
			
			delta = (y - y0) * (x1 - x0) - (x - x0) * (y1 - y0);
			
			if(delta > tolerance)
				count+=1;
		}
		return (count == numNodes);
	}
	
	/**
	 * Returns true if this Vertex is on any edge of
	 * the provided XFEMElement e or false if it isn't.
	 * @param e The provided XFEMElement e.
	 * @return True if this Vertex is on any edge of
	 * the provided XFEMElement e or false if it isn't.
	 */
	public boolean isOnEdge(XFEMElement e){
		List<IPoint3d> pts = new ArrayList<IPoint3d>();
		List<ElementNode> nodesList = e.getIncidence();
		ListIterator nodes = nodesList.listIterator();
		while(nodes.hasNext()){
			XFEMElementNode node = (XFEMElementNode) nodes.next();
			IPoint3d p = new IPoint3d(node.getX(),node.getY());
			pts.add(p);
		}
		
		IPoint3d point = new IPoint3d(this.getCoordinates(0),this.getCoordinates(1));
		boolean onEdge = false;
		boolean found = false;
		Segment s = new Segment();
		int i = 0;
		while((i<pts.size()) && (!found)){
			if(i<(pts.size()-1)){
				s.setFirstPoint((IPoint3d)pts.get(i));
				s.setSecondPoint((IPoint3d)pts.get(i+1));	
			}
			else if(i == (pts.size()-1)){
				s.setFirstPoint((IPoint3d)pts.get(i));
				s.setSecondPoint((IPoint3d)pts.get(0));
			}
			
			double x = s.getSecondPoint().getX()-s.getFirstPoint().getX();
			double y = s.getSecondPoint().getY()-s.getFirstPoint().getY();
			IPoint3d vector = new IPoint3d(x,y);
			s.setVector(vector);
			
			if(s.on(point)){
				found = true;
				onEdge = true;
			}
			i++;
		}
		
		return onEdge;
	}
	
	/**
	 * Returns true if this vertex coincide with provided
	 * node or false if it don't coincide.
	 * @param node The provided XFEMElementNode node.
	 * @return True if this vertex coincide with provided
	 * node or false if it don't coincide.
	 */
	public boolean isOn(XFEMElementNode node){
		double tolerance = 0.000001;
		
		double aX = node.getX() + tolerance;
		double aY = node.getY();
		double bX = node.getX() - tolerance;
		double bY = node.getY();
		double cX = node.getX();
		double cY = node.getY() + tolerance;
		
		double dX = this.getCoordinates(0);
		double dY = this.getCoordinates(1);
		
		IMatrix incircle = new IMatrix(4,4);
		incircle.setElement(0,0,aX);
		incircle.setElement(0,1,aY);
		incircle.setElement(0,2,Math.pow(aX,2.0) + Math.pow(aY,2.0));
		incircle.setElement(0,3,1.0);
		incircle.setElement(1,0,bX);
		incircle.setElement(1,1,bY);
		incircle.setElement(1,2,Math.pow(bX,2.0) + Math.pow(bY,2.0));
		incircle.setElement(1,3,1.0);
		incircle.setElement(2,0,cX);
		incircle.setElement(2,1,cY);
		incircle.setElement(2,2,Math.pow(cX,2.0) + Math.pow(cY,2.0));
		incircle.setElement(2,3,1.0);
		incircle.setElement(3,0,dX);
		incircle.setElement(3,1,dY);
		incircle.setElement(3,2,Math.pow(dX,2.0) + Math.pow(dY,2.0));
		incircle.setElement(3,3,1.0);
		
		double det = incircle.determinantLU();
		
		boolean result = false;
		if(det < 0.000000)
			result = true;
		
		return result;
	}

}
